﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "ExprToken.h"

#include <cwctype>
#include <regex>

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

void ExprTokenScanner::SetInputString(wchar_t* pInputStr)
{
    m_inputStr = pInputStr;
    m_position = 0;
    m_length = static_cast<int>(wcslen(pInputStr));
}

ExprTokenType ExprTokenScanner::GetToken()
{
    StateType state = StateType::Start;
    ExprTokenType currentToken;

    m_tokenStr.clear();

    while (state != StateType::Done)
    {
        bool needSave = true;
        int c = GetNextChar();
        switch (state)
        {
        case StateType::Start:
            if (iswspace(c))
                continue;
            else if (iswdigit(c))
                state = StateType::InInteger;
            else if (iswalpha(c) || c == L'（' || c == L'）')
                state = StateType::InID;
            else if (c == L'\'')
            {
                state = StateType::InString;
                needSave = false;
            }
            else if (c == L'$')
            {
                m_tokenStr.push_back(c);
                c = GetNextChar();
                if (c == L'(')
                {
                    state = StateType::InIDRef;
                }
                else
                {
                    UngetNextChar();
                    currentToken = ExprTokenType::Error;
                }
            }
            else
            {
                state = StateType::Done;
                if (c == EOF_STRING)
                    currentToken = ExprTokenType::EndInput;
                else if (c == L'+')
                    currentToken = ExprTokenType::Plus;
                else if (c == L'-')
                    currentToken = ExprTokenType::Minus;
                else if (c == L'*')
                    currentToken = ExprTokenType::Times;
                else if (c == L'/')
                    currentToken = ExprTokenType::Over;
                else if (c == L'%')
                    currentToken = ExprTokenType::Modulus;
                else if (c == L'(')
                    currentToken = ExprTokenType::LParen;
                else if (c == L')')
                    currentToken = ExprTokenType::RParen;
                else if (c == L',')
                    currentToken = ExprTokenType::Comma;
                else if (c == L'=')
                    currentToken = ExprTokenType::EQ;
                else if (c == L'<')
                {
                    m_tokenStr.push_back(c);
                    c = GetNextChar();
                    if (c == L'=')
                        currentToken = ExprTokenType::LE;
                    else if (c == L'>')
                        currentToken = ExprTokenType::NotEQ;
                    else
                    {
                        currentToken = ExprTokenType::LT;
                        UngetNextChar();
                        needSave = false;
                    }
                }
                else if (c == L'>')
                {
                    m_tokenStr.push_back(c);
                    c = GetNextChar();
                    if (c == L'=')
                        currentToken = ExprTokenType::GE;
                    else
                    {
                        currentToken = ExprTokenType::GT;
                        UngetNextChar();
                        needSave = false;
                    }
                }
                else
                {
                    currentToken = ExprTokenType::Error;
                }
            }

            break;
        case StateType::InInteger:
            if (c == L'.')
            {
                state = StateType::InFloat;
            }
            else if (!iswdigit(c))
            {
                state = StateType::Done;
                UngetNextChar();
                currentToken = ExprTokenType::Integer;
                needSave = false;
            }
            break;
        case StateType::InFloat:
            if (!iswdigit(c))
            {
                state = StateType::Done;
                UngetNextChar();
                currentToken = ExprTokenType::Float;
                needSave = false;
            }
            break;
        case StateType::InID:
            if (!iswalnum(c) && (c != 95) && (c != L'（') && (c != L'）')) // 如果不为数字、字母、下划线、中文左括号“（”及右括号“）”
            {
                state = StateType::Done;
                UngetNextChar();
                currentToken = ExprTokenType::ID;
                needSave = false;
            }
            break;
        case StateType::InString:
            if (c == L'\'' || c == EOF_STRING)
            {
                state = StateType::Done;
                currentToken = ExprTokenType::String;
                needSave = false;
            }
            break;
        case StateType::InIDRef:
            if (c == L')')
            {
                state = StateType::Done;
                currentToken = ExprTokenType::IDRef;
            }
            break;
        default:
            break;
        } // end switch
        if (needSave)
            m_tokenStr.push_back(c);
    } // end while

    m_tokenStr.push_back(L'\0');

    // 从ExprTokenType::ID区分出And、Or、Not、True、False 和嵌套表达式
    if (currentToken == ExprTokenType::ID)
    {
        ExprTokenType spectialToken;
        spectialToken = FindSpecialToken(m_tokenStr.data());
        if (spectialToken != ExprTokenType::Error)
            currentToken = spectialToken;
        else if (m_pEngine->FindParser(m_tokenStr.data()))
            currentToken = ExprTokenType::ExprPaser;
        else if (m_pEngine->FindFunctionAndAdjustName(m_tokenStr.data()))
        {
            currentToken = ExprTokenType::Function;
            // 调整函数名的大小写格式
            int strLen = (int)wcslen(m_tokenStr.data());
            if (m_position - strLen >= 0)
            {
                std::wstring tempStr(m_inputStr);
                tempStr.replace(m_position - strLen, strLen, m_tokenStr.data());
                wcscpy(m_inputStr, tempStr.c_str());
            }
        }
    }
    else if (currentToken == ExprTokenType::IDRef)
    {
        std::wstring idRefName = GetRefIDTokenString();
        if (m_pEngine->FindParser(idRefName.data()))
        {
            currentToken = ExprTokenType::ExprPaserRef;
        }
    }
    return currentToken;
}

const wchar_t* ExprTokenScanner::GetTokenString()
{
    return m_tokenStr.data();
}

std::wstring ExprTokenScanner::GetRefIDTokenString() const
{
    static std::wregex reg(L"^(?:\\$\\(\\s*)(.*?)(?:\\s*\\))$");
    std::wcmatch match;
    if (std::regex_match(m_tokenStr.data(), match, reg))
    {
        if (match.size() > 1)
            return match.str(1);
    }
    return std::wstring();
}

int ExprTokenScanner::GetNextChar()
{
    if (m_position < m_length)
        return m_inputStr[m_position++];
    else
    {
        m_position++;
        return EOF_STRING;
    }
}

void ExprTokenScanner::UngetNextChar()
{
    m_position--;
}

ExprTokenType ExprTokenScanner::FindSpecialToken(wchar_t* pTokenString)
{
    std::map<std::wstring, ExprTokenType>::iterator iter = s_specialTokenContainer.find(pTokenString);
    if (iter != s_specialTokenContainer.end())
        return iter->second;
    else
        return ExprTokenType::Error;
}

void ExprTokenScanner::TrimTokenString()
{
    size_t rightTrim = m_tokenStr.size();
    for (size_t i = m_tokenStr.size() - 1; i >= 0; i--)
    {
        const wchar_t& c = m_tokenStr[i];
        if (c != L' ')
        {
            rightTrim = i;
            break;
        }
    }

    if (rightTrim < m_tokenStr.size() - 1)
        m_tokenStr.erase(m_tokenStr.begin() + rightTrim + 1, m_tokenStr.end());

    size_t leftTrim = -1;
    for (size_t i = 0; i < m_tokenStr.size(); i++)
    {
        const wchar_t& c = m_tokenStr[i];
        if (c != L' ')
        {
            leftTrim = i;
            break;
        }
    }

    if (leftTrim > 0)
        m_tokenStr.erase(m_tokenStr.begin(), m_tokenStr.begin() + leftTrim);
}

ExprTokenScanner::ExprTokenScanner(IExprEngine* pExprEngine)
{
    m_pEngine = pExprEngine;
    if (s_specialTokenContainer.empty())
    {
        std::pair<std::wstring, ExprTokenType> token;
        token.first = L"And";
        token.second = ExprTokenType::And;
        s_specialTokenContainer.insert(token);
        token.first = L"and";
        token.second = ExprTokenType::And;
        s_specialTokenContainer.insert(token);
        token.first = L"Or";
        token.second = ExprTokenType::Or;
        s_specialTokenContainer.insert(token);
        token.first = L"or";
        token.second = ExprTokenType::Or;
        s_specialTokenContainer.insert(token);
        token.first = L"Not";
        token.second = ExprTokenType::Not;
        s_specialTokenContainer.insert(token);
        token.first = L"not";
        token.second = ExprTokenType::Not;
        s_specialTokenContainer.insert(token);
        token.first = L"True";
        token.second = ExprTokenType::True;
        s_specialTokenContainer.insert(token);
        token.first = L"true";
        token.second = ExprTokenType::True;
        s_specialTokenContainer.insert(token);
        token.first = L"False";
        token.second = ExprTokenType::False;
        s_specialTokenContainer.insert(token);
        token.first = L"false";
        token.second = ExprTokenType::False;
        s_specialTokenContainer.insert(token);
    }

    m_inputStr = nullptr;
    m_length = 0;
    m_position = 0;

    m_tokenStr.reserve(256);
}

const int ExprTokenScanner::GetCurrentTokenStartPosition() const
{
    return m_position - static_cast<int>(m_tokenStr.size()) + 1;
}

const int ExprTokenScanner::GetCurrentTokenEndPosition() const
{
    return m_position;
}

std::map<std::wstring, ExprTokenType> ExprTokenScanner::s_specialTokenContainer = std::map<std::wstring, ExprTokenType>();
